const std = @import("std");
const builtin = @import("builtin");
const string = @import("../string/string.zig");
const String = string.String;
const thread = @import("../thread/thread.zig");

// %q 消息级别首个大写字母  T D I W E A
// %Q 消息级别 Trace Debug Info Warn Error Abort
// %a 线程ID
// %A 线程名称
// %f 所在文件
// %l 所在文件行号
// %d 日期
// %t 时间
// %D 日期和时间
// %s 日志内容

pub const LogLevel = enum {
    Debug,
    Info,
    Warn,
    Error,
};

pub const LogOutType = enum {
    Console,
    File,
    ConsoleAndFile,
};

pub const LogConfig = struct {
    level: LogLevel,
    file_path: ?String,
    format_str: String,
    output: LogOutType,
    allocator: std.mem.Allocator,
    pub fn init(allocator: std.mem.Allocator, file_path: []const u8) LogConfig {
        const temp_str = toAbsoluteFile(allocator, file_path) catch {
            @panic("获取日志文件绝对路径失败");
        };
        var ret = .{
            .level = .Debug,
            .file_path = temp_str,
            .format_str = String.init(allocator),
            .output = .ConsoleAndFile,
            .allocator = allocator,
        };

        ret.format_str.setNeedSize(50) catch {
            @panic("格式化字符串分配内存失败");
        };
        ret.format_str.pushBackCharArray("%q %t [%a]%f(%l): %s") catch {
            @panic("内存分配错误");
        };

        return ret;
    }

    pub fn deinit(self: LogConfig) void {
        if (self.file_path) |data| {
            data.deinit();
        }
        self.format_str.deinit();
    }
};

fn currentDir(allocator: std.mem.Allocator, ret: *String) !void {
    ret.clear();
    const work_dir = std.fs.cwd();
    const abs_file = try work_dir.realpathAlloc(allocator, ".");
    defer allocator.free(abs_file);
    try ret.pushBackCharArray(abs_file);
}

fn updateDirItem(allocator: std.mem.Allocator, ret: *String) !void {
    const dirItems = ret.split("/");
    defer string.freeStringArray(dirItems);
    ret.clear();
    var id_list = std.ArrayList(usize).init(allocator);
    defer id_list.deinit();
    for (dirItems.items, 0..dirItems.items.len) |name, i| {
        if (name.equal(".")) {
            continue;
        } else if (name.equal("..")) {
            _ = id_list.pop();
        } else {
            id_list.append(i) catch {
                return error.IceRunTimeError;
            };
        }
    }
    if (builtin.os.tag == std.Target.Os.Tag.linux) {
        try ret.pushBackChar('/');
    }
    for (id_list.items) |i| {
        try ret.pushBackStr(dirItems.items[i]);
        try ret.pushBackChar('/');
    }
    _ = try ret.popBack();
}

fn toAbsoluteFile(allocator: std.mem.Allocator, file_path: []const u8) !String {
    var ret = String.init(allocator);
    if (!std.fs.path.isAbsolute(file_path)) {
        try currentDir(allocator, &ret);
        try ret.replaceAll("\\", "/");
        const ch = try ret.at(-1);
        if (ch != '/') {
            try ret.pushBackChar('/');
        }
        try ret.pushBackCharArray(file_path);
        try ret.replaceAll("\\", "/");
    } else {
        try ret.pushBackCharArray(file_path);
        try ret.replaceAll("\\", "/");
    }
    // 文件夹路径
    try updateDirItem(allocator, &ret);
    const dir_path = std.fs.path.dirname(ret.toStr());
    if (dir_path) |new_path| {
        _ = std.fs.openDirAbsolute(new_path, .{}) catch |err| {
            switch (err) {
                error.FileNotFound => {
                    try std.fs.makeDirAbsolute(new_path);
                },
                else => return err,
            }
        };
    }
    return ret;
}
fn threadIdStr(allocator: std.mem.Allocator) !String {
    var ret = String.init(allocator);
    try ret.setNeedSize(20);
    try ret.pushBackFormat("{}", .{std.Thread.getCurrentId()});
    return ret;
}

pub const Logger = struct {
    config: LogConfig,
    file: ?std.fs.File,
    mutex: std.Thread.Mutex,

    /// 单例实例
    var instance: *Logger = undefined;
    var init_call: bool = false;
    var instance_mutex: std.Thread.Mutex = .{};

    pub fn init(config: LogConfig) !void {
        instance_mutex.lock();
        defer instance_mutex.unlock();

        if (init_call) {
            return error.IceAlreadyInitialized; // 防止重复初始化
        }
        instance = try config.allocator.create(Logger);
        instance.mutex = .{};
        instance.config = config;
        instance.file = null;
        init_call = true;

        switch (config.output) {
            .File, .ConsoleAndFile => {
                if (config.file_path) |log_file| {
                    const file = try std.fs.createFileAbsolute(log_file.toStr(), .{});
                    instance.file = file;
                } else {
                    return error.IceNotFindError;
                }
            },
            else => {
                return error.IceNotFindError;
            },
        }
    }

    pub fn getInstance() *Logger {
        instance_mutex.lock();
        defer instance_mutex.unlock();

        if (init_call) {
            return instance;
        } else {
            @panic("Logger not initialized");
        }
        // return &instance orelse @panic("Logger not initialized");
    }

    /// 释放日志器资源
    pub fn deinit(self: *Logger) void {
        instance_mutex.lock();
        defer instance_mutex.unlock();

        if (self.file) |log_file| {
            log_file.close();
        }
        self.config.deinit();
        self.config.allocator.destroy(instance);
        init_call = false;
    }

    /// 写入日志消息
    pub fn log(self: *Logger, src: std.builtin.SourceLocation, level: LogLevel, comptime format: []const u8, args: anytype) !void {
        self.mutex.lock();
        defer self.mutex.unlock();

        if (@intFromEnum(level) < @intFromEnum(self.config.level)) {
            return; // 如果日志级别低于当前级别，则忽略
        }
        var log_message = String.init(self.config.allocator);
        defer log_message.deinit();
        try log_message.setNeedSize(1024);
        var change = false;
        for (self.config.format_str.toStr()) |ch| {
            if (change) {
                switch (ch) {
                    '%' => { // 打印%号
                        try log_message.pushBackChar(ch);
                    },
                    'q' => { // 消息级别
                        try log_message.pushBackCharArray(switch (level) {
                            .Debug => "D",
                            .Info => "I",
                            .Warn => "W",
                            .Error => "E",
                        });
                    },
                    'Q' => { // 消息级别
                        try log_message.pushBackCharArray(switch (level) {
                            .Debug => "DEBUG",
                            .Info => "INFO",
                            .Warn => "WARN",
                            .Error => "ERROR",
                        });
                    },
                    'a' => { // 线程ID
                        const thread_id = try threadIdStr(self.config.allocator);
                        defer thread_id.deinit();
                        try log_message.pushBackStr(thread_id);
                    },
                    'A' => {
                        try log_message.pushBackCharArray(thread.getThreadName());
                    },
                    'f' => { // 文件名
                        try log_message.pushBackCharArray(src.file);
                    },
                    'l' => { // 文件行号
                        try log_message.pushBack(u32, src.line);
                    },
                    'd' => { // 日期
                        const temp_date = nowDateTime(self.config.allocator, .Date);
                        defer temp_date.deinit();
                        if (!temp_date.isEmpty()) {
                            try log_message.pushBackStr(temp_date);
                        }
                    },
                    't' => { // 时间
                        const temp_date = nowDateTime(self.config.allocator, .Time);
                        defer temp_date.deinit();
                        if (!temp_date.isEmpty()) {
                            try log_message.pushBackStr(temp_date);
                        }
                    },
                    'D' => { // 日期和时间
                        const temp_date = nowDateTime(self.config.allocator, .DateTime);
                        defer temp_date.deinit();
                        if (!temp_date.isEmpty()) {
                            try log_message.pushBackStr(temp_date);
                        }
                    },
                    's' => { // 日志内容
                        try log_message.pushBackFormat(format, args);
                    },
                    else => {
                        try log_message.pushBackChar('%');
                        try log_message.pushBackChar(ch);
                    },
                }
                change = false;
            } else {
                if (ch == '%') {
                    change = true;
                } else {
                    try log_message.pushBackChar(ch);
                }
            }
        }
        try log_message.pushBackChar('\n');
        switch (self.config.output) {
            .Console => {
                const stderr = std.io.getStdErr().writer();
                stderr.writeAll(log_message.toStr()) catch {
                    return error.IceConsoleOutputError;
                };
            },
            .File => {
                if (self.file) |log_file| {
                    log_file.writeAll(log_message.toStr()) catch {
                        return error.IceFileOutputError;
                    };
                }
            },
            .ConsoleAndFile => {
                const stderr = std.io.getStdErr().writer();
                stderr.writeAll(log_message.toStr()) catch {
                    return error.IceConsoleOutputError;
                };
                if (self.file) |log_file| {
                    log_file.writeAll(log_message.toStr()) catch {
                        return error.IceFileOutputError;
                    };
                }
            },
        }
    }
};

const DateType = enum {
    Date,
    Time,
    DateTime,
};

// 手动声明 SYSTEMTIME 结构体
const SYSTEMTIME = extern struct {
    wYear: c_ushort,
    wMonth: c_ushort,
    wDayOfWeek: c_ushort,
    wDay: c_ushort,
    wHour: c_ushort,
    wMinute: c_ushort,
    wSecond: c_ushort,
    wMilliseconds: c_ushort,
};
// 手动声明 GetLocalTime 函数
extern "kernel32" fn GetLocalTime(lpSystemTime: *SYSTEMTIME) callconv(.C) void;

fn nowDateTime(allocator: std.mem.Allocator, date_type: DateType) String {
    if (builtin.os.tag == std.Target.Os.Tag.windows) {
        return nowDateTimeWindows(allocator, date_type);
    } else {
        @panic("获取时间的函数未实现");
    }
}

fn nowDateTimeWindows(allocator: std.mem.Allocator, date_type: DateType) String {
    var st: SYSTEMTIME = undefined;
    GetLocalTime(&st);
    var ret = String.init(allocator);
    switch (date_type) {
        .Date => {
            ret.setNeedSize(20) catch {
                return ret;
            };
            ret.pushBackFormat("{}-{:0>2}-{:0>2}", .{ st.wYear, st.wMonth, st.wDay }) catch {
                return ret;
            };
            return ret;
        },
        .Time => {
            ret.setNeedSize(20) catch {
                return ret;
            };
            ret.pushBackFormat("{:0>2}:{:0>2}:{:0>2}.{:0>3}", .{ st.wHour, st.wMinute, st.wSecond, st.wMilliseconds }) catch {
                return ret;
            };
            return ret;
        },
        .DateTime => {
            ret.setNeedSize(30) catch {
                return ret;
            };
            ret.pushBackFormat("{}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2}.{:0>3}", .{ st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds }) catch {
                return ret;
            };
            return ret;
        },
    }
}
pub const log = struct {
    pub fn debug(src: std.builtin.SourceLocation, comptime format: []const u8, args: anytype) !void {
        try Logger.getInstance().log(src, .Debug, format, args);
    }
    pub fn info(src: std.builtin.SourceLocation, comptime format: []const u8, args: anytype) !void {
        try Logger.getInstance().log(src, .Info, format, args);
    }
    pub fn warn(src: std.builtin.SourceLocation, comptime format: []const u8, args: anytype) !void {
        try Logger.getInstance().log(src, .Warn, format, args);
    }
    pub fn err(src: std.builtin.SourceLocation, comptime format: []const u8, args: anytype) !void {
        try Logger.getInstance().log(src, .Error, format, args);
    }
};
